package cpa.prowon;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Properties;
import java.util.ResourceBundle;

import org.eclipse.swt.widgets.Display;

import pho.prowon.category.IndexCategoryDefinitionFactory;
import pho.prowon.index.IndexXMLImplementation;
import cpa.prowon.gui.MainWindow;

/**
 * The Application class is the entry point for the whole application. The
 * object will be a sole instance and can be accessed via Application.getInstance().
 * It provides access to the internal and application properties as well as the
 * language resource bundle.
 * When starting the application, the appropriate GUI will be started.
 * @author Chris
 */
public class Application {

	// Properties
	private static Application _instance;
	private ResourceBundle resourceBundle;
	private Properties intProps;
	private Properties appProps;
	
	/**
	 * Property name constant for the application version
	 * @return The property name for the application version
	 * @author Chris
	 */
	public static final String PROPNAME_APPLICATION_VERSION = "prowon.Application.Version";

	/**
	 * Property name constant for the application name
	 * @return The property name for the application name
	 * @author Chris
	 */
	public static final String PROPNAME_APPLICATION_NAME = "prowon.Application.Name";

	/**
	 * Property name constant for the language resource bundle file prefix
	 * @return The property name for the resource bundle file prefix
	 * @author Chris
	 */
	protected static final String PROPNAME_I18N_FILEPATH = "prowon.I18N.Filepath";

	/**
	 * Property name constant for the file path to the default configuration file
	 * @return The property name for the file path to the default configuration file
	 * @author Chris
	 */
	protected static final String PROPNAME_CONFIGURATION_DEFAULTFILEPATH = "prowon.Configuration.DefaultFilePath";

	/**
	 * Property name constant for the file path to the application configuration file
	 * @return The property name for the file path to the application configuration file
	 * @author Chris
	 */
	protected static final String PROPNAME_CONFIGURATION_APPLICATIONFILEPATH = "prowon.Configuration.ApplicationFilePath";

	/**
	 * Property name constant for the index category definition name property. This property
	 * is used to remember the last used index category defintion
	 * @return The property name for the index category definition
	 * @author Chris
	 */
	public static final String PROPNAME_INDEXCATEGORYDEFINITION_NAMEPROPERTY = "prowon.IndexCategoryDefinition.NameProperty";

	/**
	 * Property name constant for the index category definition name. This property
	 * is used to remember the last used index category defintion
	 * @return The property name for the index category definition
	 * @author Chris
	 */
	public static final String PROPNAME_INDEXCATEGORYDEFINITION_NAME = "prowon.IndexCategoryDefinition.Name";

	/**
	 * Command line override identifier for the index location
	 * @return The command line override identifier for the index location
	 * @author Chris
	 */
	protected static final String CL_OVERRIDE_INDEXLOCATION = "-i";

	/**
	 * Constructor for the Application. The constructor is protected to avoid that more
	 * than one instance can be created.
	 * @author Chris
	 */
	protected Application() {

		// Run the constructor of the super class
		super();

		// Do the initialization
		initialize();
		
	}

	/**
	 * Constructor for the Application. The constructor is protected to avoid that more
	 * than one instance can be created. Command line arguments can be passed to overwrite
	 * default settings
	 * @param String[] Command line arguments
	 * @author Chris
	 */
	private Application(String[] args) {

		// Call the simple constructor
		this();
		
		// Overwrite properties with system properties
		initializeFromCommandLine(args);
		
	}

	/**
	 * Do the initialization using the command line parameters. These paramaters
	 * can be used to overwrite the default settings of the application.
	 * @param String[] The command line arguments which got passed when calling
	 * the Application. Currently, following command line overrides are allowed:
	 * -i indexfile
	 * @author Chris
	 */
	private void initializeFromCommandLine(String args[]) {
		
		// Check whether or not command line arguments were supplied
		if (args.length > 0) {
			 
			// Get an array of the values
			ArrayList argColl = new ArrayList();
			for (int i=1; i<=args.length; i++) {
				argColl.add(args[i-1]);
			}
			
			// Set the index overwrite if available
			// The index override is specified via -i
			int io_position = argColl.indexOf(Application.CL_OVERRIDE_INDEXLOCATION);
			if (io_position != -1) {
				
				// Get the index file name
				if (argColl.size() < io_position+2) {
					System.out.println(getResourceBundle().getString("msg_cmdline_options_insufficientargument_indexlocation"));
				} else {
					String io = (String) argColl.get(io_position+1);
					getApplicationProperties().setProperty(IndexXMLImplementation.PROPNAME_INDEXLOCATION, io);
					System.setProperty(IndexXMLImplementation.PROPNAME_INDEXLOCATION, io);
					System.out.println(getResourceBundle().getString("msg_cmdline_override_indexlocation") + io);
				}

			}
			
		}
		
	}

	/**
	 * Initialize the internal and application properties as well as the resource bundle.
	 * For each of those initialization areas an own intialization routine is used
	 * @author Chris
	 */
	private void initialize() {

		// Initialize the internal properties
		initializeInternalProperties();
		
		// Initialize the application properties
		initializeApplicationProperties();
		
		// Initialize the language
		initializeResourceBundle();
		
	}
	
	/**
	 * Initialize the resource bundle by using the language resource bundle
	 * file path specified in the internal configuration
	 * @author Chris
	 */
	private void initializeResourceBundle() {
		
		//@TODO Make resource bundles jar independent
		// Set the language patch based on the system language and country
		resourceBundle = ResourceBundle.getBundle(intProps.getProperty(Application.PROPNAME_I18N_FILEPATH), Locale.getDefault());

	}

	/**
	 * Initialize the internal properties. All internal properties have to be defined here
	 * @author Chris
	 */
	private void initializeInternalProperties() {
		
		// Set the internal properties
		intProps = new Properties();
		
		// Set the internal defined properties
		intProps.setProperty(Application.PROPNAME_APPLICATION_NAME,"Prowon");
		intProps.setProperty(Application.PROPNAME_APPLICATION_VERSION,"0.70 (Snoopy 0612363804)");
		intProps.setProperty(Application.PROPNAME_I18N_FILEPATH,"lang/lang");
		intProps.setProperty(Application.PROPNAME_CONFIGURATION_DEFAULTFILEPATH,"conf/default.conf");
		intProps.setProperty(Application.PROPNAME_CONFIGURATION_APPLICATIONFILEPATH,"conf/application.conf");
		intProps.setProperty(IndexCategoryDefinitionFactory.PROPNAME_CATEGORYDEFINITION_FILEPATH, "catdef");
		
		// Provide the internal properties as system properties
		Enumeration propNames = intProps.propertyNames();
		while (propNames.hasMoreElements()) {
			String propName = (String) propNames.nextElement();
			System.setProperty(propName, intProps.getProperty(propName));
		}
		
	}

	/**
	 * Initialize the application properties by using the internally specified path to the
	 * configuration files
	 * @author Chris
	 */
	private void initializeApplicationProperties() {

		// Create and load the default properties
		Properties defaultProps = new Properties();
		try {
			FileInputStream in = new FileInputStream(intProps.getProperty(Application.PROPNAME_CONFIGURATION_DEFAULTFILEPATH));
			defaultProps.load(in);
			in.close();
		} catch (Exception e) {
			System.out.println(e);
		}

		// Create the program properties with defaults
		appProps = new Properties(defaultProps);

		// Now load the properties from last invocation
		try {
			FileInputStream in = new FileInputStream(intProps.getProperty(Application.PROPNAME_CONFIGURATION_APPLICATIONFILEPATH));
			appProps.load(in);
			in.close();
		} catch (Exception e) {
			System.out.println(e);
		}
		
		// Provide the application properties also as system properties
		Enumeration propNames = appProps.propertyNames();
		while (propNames.hasMoreElements()) {
			String propName = (String) propNames.nextElement();
			System.setProperty(propName, appProps.getProperty(propName));
		}
		
	}

	/**
	 * Save the application properties by using the specified file paths to the default
	 * and application configuration files
	 * @author Chris
	 */
	public void saveApplicationProperties() {
		
		try {
			
			// Create the file output streams and save the configuration
			FileOutputStream out = new FileOutputStream(intProps.getProperty(Application.PROPNAME_CONFIGURATION_APPLICATIONFILEPATH));
			appProps.store(out, intProps.getProperty(Application.PROPNAME_APPLICATION_NAME) + " " + intProps.getProperty(Application.PROPNAME_APPLICATION_VERSION));

		} catch (IOException e) {
			
			// Show a console message
			System.out.println("Couldn't save application properties");
			System.out.println(e.getMessage());
		}

	}
	
	/**
	 * Return the one and only instance of the application. If no object exists yet, a
	 * new one will be created and will be accessible next time this get routine is used
	 * @return Application The sole instance of Application
	 * @author Chris
	 */
	public static Application getInstance() {
		
		// If the instance does not exist yet, create it
		if (_instance == null ) {
			_instance = new Application();
		}
		
		// Return the instance
		return _instance;
		
	}

	/**
	 * Return the application properties
	 * @return Properties The application properties
	 * @author Chris
	 */
	public  Properties getApplicationProperties() {
		return appProps;
	}
	
	/**
	 * Return the internal properties
	 * @return Properties The internal properties
	 * @author Chris
	 */
	public Properties getInternalProperties() {
		return intProps;
	}
	
	/**
	 * Return the language resource bundle
	 * @return ResourceBundle The resource bundle using the i18n information in the application properties
	 * @author Chris
	 */
	public ResourceBundle getResourceBundle() {
		return resourceBundle;
	}

	/**
	 * The main routine is used to start the application. By starting the application, all properties
	 * and resource bundes will be initialized and the appropriate GUI will be opened
	 * @param String[] Command line arguments
	 * @author Chris
	 */
	public static void main(String[] args) {
		
		// Initialize the instance
		_instance = new Application(args);
		
		// Open the main window
		MainWindow w = MainWindow.getInstance();
		w.setBlockOnOpen( true );
		w.open();
		
		// Dispose the widgets and clipboard contents when the window gets closed
		Display.getCurrent().dispose();
		Util.getClipboard().dispose();
		
	}

}
